/*
 * Scrabble.cpp
 *
 *  Created on: Jun 28, 2009
 *      Author: mlrus
 */

#include "Axis.h"
#include "Common.h"
#include "Scrabble.h"
#include "ExpansionOP.h"

#include <string>
#include <algorithm>
#include <climits>
#include <iomanip>

char Scrabble::wordBuffer[Common::boardSize + 1];
bool Scrabble::checkWord(const Cell *from, const Cell *to, Axis::Axis axis) {
	uint incr = board.getIncrement(axis);
	char *wptr = wordBuffer;
	for (const Cell *cell = from; cell <= to; cell = cell + incr) {
		*wptr++ = cell->letter;
	}
	*wptr = 0;
	return dictionary.contains(wordBuffer);
}

const char* Scrabble::getWordbuffer() {
	return wordBuffer;
}

const Axis::Axis Scrabble::perpendicular(Axis::Axis axis) {
	return axis == Axis::HORIZONTAL ? Axis::VERTICAL : (axis == Axis::VERTICAL ? Axis::HORIZONTAL : Axis::POINT);
}

const bool Scrabble::checkConstraints(const Cell* cell, Axis::Axis axis) {
	Cell *from, *to;
	if (cell->isFilled() && board.getEndpoints(cell, axis, from, to))
		return checkConstraints(from, to, axis);
	return false;
}

const inline bool Scrabble::checkConstraints(const Cell *from, const Cell *to, Axis::Axis axis) {
	bool res = false;
	char vbuff[Common::boardSize + 1], *vptr = vbuff;
	if (from == to)
		res = true;
	else {
		uint incr = board.getIncrement(axis);
		for (const Cell *cell = from; cell <= to; cell += incr) {
			*vptr++ = cell->letter;
		}
		*vptr = 0;
		res = dictionary.contains(vbuff);
	}
	return res;
}

int getBestScore() {
	int bestScore = 0;
	return bestScore;
}

int Scrabble::playRecursive(Cell *cell, Cell* head, Cell *tail, const char *rack, Axis::Axis axis, int *dirs) {
	if (rack == NULL || *rack == 0)
		return 0;
	int res = 0;
	char nextRack[Common::rackSize];
	char *nextRackPtr = nextRack;
	int racksize = strlen(rack);
	*nextRackPtr = 0;
	Axis::Axis dual = Axis::getDual(axis);
	int dir = *dirs;
	int nextDir = *(dirs + 1);
	Cell *nextHead = cell, *nextTail = cell;

	cell->set(*rack);
	board.getEndpoints(axis, head, tail);

	if (strlen(rack) > 1) {
		nextHead = head;
		nextTail = tail;

		if (dir < 0) {
			while (!nextHead->isEndpoint(dir) && nextHead->isFilled()) {
				nextHead += dir;
			}
			if (nextDir != 0) {
				while (!nextTail->isEndpoint(nextDir) && nextTail->isFilled()) {
					nextTail += nextDir;
				}
			}
		} else {
			if (dir > 0) {
				while (!nextTail->isEndpoint(dir) && nextTail->isFilled()) {
					nextTail += dir;
				}
			}
		}

		if (nextHead->isFilled())
			nextHead = NULL;
		if (nextTail->isFilled())
			nextTail = NULL;
	}

	// iterate over each character in the rack
	for (const char *ch = rack; *ch != 0; ch++) {
		nextRackPtr = nextRack;
		*nextRackPtr = 0;
		cell->set(*ch);
		if (checkConstraints(cell, dual)) {
			testBoardQuick(head, tail, cell, axis); // assertion: no tiles  before head or after tail
			if (racksize > 1) {
				if (nextHead != nextTail) {
					for (const char *p = rack; *p != 0; p++) {
						if (p != ch)
							*nextRackPtr++ = *p;
					}
					*nextRackPtr = 0;

					if (nextHead != NULL) {
						res = playRecursive(nextHead, nextHead, tail, nextRack, axis, dirs);
					}
					if (nextTail != NULL) {
						res = playRecursive(nextTail, head, nextTail, nextRack, axis, dirs + 1);
					}
				}
			}
		}
		cell->clear();
	}
	return res;
}

/***
 * Try each character in each position, and then try front/back and top/down if it works
 */

void Scrabble::evalAll() {
	Axis::Axis axes[] = { Axis::HORIZONTAL, Axis::VERTICAL };
	int dirvals[][3] = { { -1, 1, 0} , { -15, 15, 0 } };
	for (Cell *cell = board.firstSquare(); cell < board.lastSquare(); cell++) {
		if (cell->isEmpty() && board.hasAdjacent(cell)) {
			for (int i = 0; i < 2; i++) {
				Axis::Axis axis = axes[i];
				int *dirs = dirvals[i];
				Cell *head, *tail;
				board.getEndpoints(cell, axis, head, tail);
				playRecursive(cell, head, tail, rack.c_str(), axis, dirs);
			}
		}
	}
	showResults();
}

// Depreciated routine. Replaced with evalAll.
void Scrabble::evalAllCostly() {
	char nextRack[Common::rackSize];
	Axis::Axis axes[] = { Axis::HORIZONTAL, Axis::VERTICAL };
	int dirvals[][3] = { { -1, 1, 0} , { -15, 15, 0 } };
	for (Cell *cell = board.firstSquare(); cell < board.lastSquare(); cell++) {
		if (cell->isEmpty() && board.hasAdjacent(cell)) {
			for (unsigned int i = 0; i < rack.length(); i++) {
				uchar ch = rack[i];
				Expander *x = *(expanderList.begin());
				Axis::Axis axes[] = { Axis::HORIZONTAL, Axis::VERTICAL };
				char *rPtr = nextRack;
				for (uint j = 0; j < i; j++)
					*rPtr++ = rack[j];
				for (uint j = i + 1; j < rack.length(); j++)
					*rPtr++ = rack[j];
				*rPtr = 0;
				for (int i = 0; i < 2; i++) {
					Axis::Axis axis = axes[i];
					int dirs[2];
					if (i == 0) {
						dirs[0] = -1;
						dirs[1] = 1;
					} else {
						dirs[0] = -15;
						dirs[1] = 15;
					}
					if (cell->validFor(ch, Axis::getDual(axis))) {

						board.setCell(cell, ch);
						Cell *from, *to;
						board.getEndpoints(cell, axis, from, to);
						x->testBoard(from, to, cell, axis);
						evalOne(cell, nextRack);
						board.retractCell(*cell);
					}
				}
			}
		}
	}
	showResults();
}

//Depreciated routine. Replaced with evalAllCostly
void Scrabble::evalMost() {
	char nextRack[Common::rackSize];
	Axis::Axis axes[] = { Axis::HORIZONTAL, Axis::VERTICAL };
	int dirvals[][3] = { { -1, 1, 0} , { -15, 15, 0 } };
	for (Cell *cell = board.firstSquare(); cell < board.lastSquare(); cell++) {
		if (cell->isEmpty() && board.hasAdjacent(cell)) {
			for (unsigned int i = 0; i < rack.length(); i++) {
				uchar ch = rack[i];
					board.setCell(cell, ch);
					evalOne(cell, nextRack);
					board.retractCell(*cell);
			}
		}
	}
	showResults();
}


int Scrabble::evalOne(const Cell* cell, char *nextRack) {
	int res = INT_MIN;
	for (vector<Expander *>::iterator it = expanderList.begin(); it < expanderList.end(); it++) {
		int v = (*it)->applyOperatorSequences(0, cell, nextRack);
		if (v > res)
			res = v;
	}
	return res;
}

void Scrabble::initCellChars() {
	for (Cell *cell = board.firstSquare(); cell < board.lastSquare(); cell++) {
		initCellChars(cell, Axis::HORIZONTAL);
		initCellChars(cell, Axis::VERTICAL);
	}
}

/***
 Precompute the valid adjacent characters along the perpendiculars
 */
void Scrabble::initCellChars(Cell *cell, Axis::Axis axis) {
	Cell *from, *to;
	char vbuff[Common::boardSize + 1];
	char *vptr = vbuff;
	uint incr = board.getIncrement(axis);
	uint32_t mask = Common::ANYLETTER;
	board.getEndpoints(cell, axis, from, to);
	if (from != to) {
		mask = 0;
		for (const Cell *c = from; c <= to; c += incr) {
			*vptr++ = c->letter;
		}
		*vptr = 0;
		char * off = vbuff + (cell - from) / incr;
		for (unsigned int i = 0; i < rack.length(); i++) {
			uchar ch = rack[i];
			*off = ch;
			if (dictionary.contains(vbuff)) {
				mask |= 0x01 << (ch - 'A');
			}
		}
	}
	cell->setValidChars(mask, axis);
}
